{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 12: Treinar uma Rede Neural criptografada com dados criptografados\n",
    "\n",
    "Neste tutorial, iremos utilizar todas as técnicas que aprendemos até agora para realizar o treinamento de uma rede neural (e a previsão) enquanto tanto o modelo quanto os dados são criptografados.\n",
    "\n",
    "Em particular, iremos apresentar nossa próprio algoritmo Autograd personalizado que funciona em computações criptografadas.\n",
    "\n",
    "Autores:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "- Jason Paumier - Github: [@Jasopaum](https://github.com/Jasopaum)\n",
    "- Théo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel)\n",
    "\n",
    "Tradução:\n",
    "- Marcus Costa - Twitter: [@marcustpv](https://twitter.com/marcustpv)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 1: Criar workers e dados de exemplo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "import syft as sy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prepare as configurações iniciais\n",
    "hook = sy.TorchHook(torch) \n",
    "\n",
    "alice = sy.VirtualWorker(id=\"alice\", hook=hook)\n",
    "bob = sy.VirtualWorker(id=\"bob\", hook=hook)\n",
    "james = sy.VirtualWorker(id=\"james\", hook=hook)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dataset de exemplo\n",
    "data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]])\n",
    "target = torch.tensor([[0],[0],[1],[1.]])\n",
    "\n",
    "# Modelo de exemplo\n",
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.fc1 = nn.Linear(2, 2)\n",
    "        self.fc2 = nn.Linear(2, 1)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = F.relu(x)\n",
    "        x = self.fc2(x)\n",
    "        return x\n",
    "model = Net()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 2: Criptografar o modelo e os dados\n",
    "\n",
    "A encriptação aqui vem em dois passos. Como a Computação Multi-Partes Segura (i.e Secure Multi-Party Computation) só funciona em números inteiros, para operar sobre números decimais (como pesos e ativações), precisamos codificar todos os nossos números usando Precisão Fixa, o que nos dará vários bits de precisão decimal. Nós fazemos isso com a chamada .fix_precision().\n",
    "\n",
    "Podemos então fazer a chamada .share() como temos para outras demonstrações, que irão encriptar todos os valores, partilhando-os entre Alice e Bob. Note que nós também definimos requires_grad para True, que também adiciona um método especial de auto gradiente para dados criptografados. De fato, como a Computação Multi-Partes Segura não funciona em valores de ponto flutuante, não podemos usar o auto gradiente do PyTorch. Portanto, precisamos adicionar um nó especial denominado AutogradTensor que calcula o grafo do gradiente através de retropropagação (i.e backpropagation). Você pode imprimir qualquer um desses elementos para ver se ele inclui um AutogradTensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Nós criptografamos tudo\n",
    "data = data.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)\n",
    "target = target.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)\n",
    "model = model.fix_precision().share(bob, alice, crypto_provider=james, requires_grad=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 3: Treinamento\n",
    "\n",
    "E agora nós podemos treinar utilizando operações básicas de tensores"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "opt = optim.SGD(params=model.parameters(),lr=0.1).fix_precision()\n",
    "\n",
    "for iter in range(20):\n",
    "    # 1) Apague todos os antigos valores de gradient (se existirem)\n",
    "    opt.zero_grad()\n",
    "\n",
    "    # 2) Faça uma predição\n",
    "    pred = model(data)\n",
    "\n",
    "    # 3) Calcule a função de perda (i.e loss function)\n",
    "    loss = ((pred - target)**2).sum()\n",
    "\n",
    "    # 4) Descubra quais pesos estão elevando a função de perda\n",
    "    loss.backward()\n",
    "\n",
    "    # 5) Mude esses pesos\n",
    "    opt.step()\n",
    "\n",
    "    # 6) Exiba o progresso\n",
    "    print(loss.get().float_precision())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A função de perda realmente diminuiu!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Impacto da precisão fixa\n",
    "\n",
    "Você pode estar se perguntando se criptografar tudo pode gerar um impacto no decrescimento da perda (i.e loss). Na verdade, como o cálculo teórico é o mesmo, os números estão muito próximos do treinamento não criptografado. Você pode verificar isso executando o mesmo exemplo sem criptografia e com uma inicialização determinística do modelo. Como esta a seguir, no método\n",
    "`__init__`:\n",
    "```\n",
    "with torch.no_grad():\n",
    "    self.fc1.weight.set_(torch.tensor([[ 0.0738, -0.2109],[-0.1579,  0.3174]], requires_grad=True))\n",
    "    self.fc1.bias.set_(torch.tensor([0.,0.1], requires_grad=True))\n",
    "    self.fc2.weight.set_(torch.tensor([[-0.5368,  0.7050]], requires_grad=True))\n",
    "    self.fc2.bias.set_(torch.tensor([-0.0343], requires_grad=True))\n",
    "```\n",
    "\n",
    "A ligeira diferença que você pode observar deve-se ao arredondamento dos valores realizados durante a transformação para precisão fixa. O valor padrão de `precision_fractional` é 3, e se você reduz para 2 a divergência com o valor original aumenta, enquanto que se você escolher `precision_fractional = 4`, a divergência  reduz."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parabéns!!! - Hora de se juntar a comunidade!\n",
    "\n",
    "Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!\n",
    "\n",
    "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n",
    "\n",
    "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Junte-se ao Slack!\n",
    "\n",
    "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Contribua com o projeto!\n",
    "\n",
    "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n",
    "\n",
    "- [Projetos do PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Doar\n",
    "\n",
    "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n",
    "\n",
    "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
